local M = {}

local uv = vim.uv or vim.loop

---@type boolean
M.is_windows = uv.os_uname().version:match("Windows")

M.is_mac = uv.os_uname().sysname == "Darwin"

---@type string
M.sep = M.is_windows and "\\" or "/"

---@param ... string
M.join = function(...)
	return table.concat({ ... }, M.sep)
end

M.is_absolute = function(path)
	if M.is_windows then
		return path:lower():match("^%a:")
	else
		return vim.startswith(path, "/")
	end
end

M.abspath = function(path)
	if not M.is_absolute(path) then
		path = vim.fn.fnamemodify(path, ":p")
	end
	return path
end

---Returns true if candidate is a subpath of root, or if they are the same path.
---@param root string
---@param candidate string
---@return boolean
M.is_subpath = function(root, candidate)
	if candidate == "" then
		return false
	end
	root = vim.fs.normalize(M.abspath(root))
	-- Trim trailing "/" from the root
	if root:find("/", -1) then
		root = root:sub(1, -2)
	end
	candidate = vim.fs.normalize(M.abspath(candidate))
	if M.is_windows then
		root = root:lower()
		candidate = candidate:lower()
	end
	if root == candidate then
		return true
	end
	local prefix = candidate:sub(1, root:len())
	if prefix ~= root then
		return false
	end

	local candidate_starts_with_sep = candidate:find("/", root:len() + 1, true) == root:len() + 1
	local root_ends_with_sep = root:find("/", root:len(), true) == root:len()

	return candidate_starts_with_sep or root_ends_with_sep
end

---Create a relative path from the source to the target
---@param source string
---@param target string
---@return string
M.relative_path = function(source, target)
	source = M.abspath(source)
	target = M.abspath(target)
	local path = {}
	while not M.is_subpath(source, target) do
		table.insert(path, "..")
		local new_source = vim.fs.dirname(source)

		-- If source is a root directory, we can't go up further so there is no relative path to the
		-- target. This should only happen on Windows, which prohibits relative paths between drives.
		if source == new_source then
			local log = require("mcf.log")
			log.warn("Could not find relative path from %s to %s", source, target)
			return target
		end

		source = new_source
	end

	local offset = vim.endswith(source, M.sep) and 1 or 2
	local rel_target = target:sub(source:len() + offset)
	table.insert(path, rel_target)
	return M.join(unpack(path))
end

return M
